package com.cucheng.chatroom.web.websocket;

import org.springframework.stereotype.Component;

import com.cucheng.chatroom.util.Constant;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;

@Component
@Sharable
public class HttpRequestHandler extends SimpleChannelInboundHandler<Object> {

    /**
     *@Description: TODO(读取完连接的消息后，对消息进行处理。
     * *这里仅处理HTTP请求，WebSocket请求交给下一个处理器。) 
     *@Author: wangchengjie
     *@Since: 2019年3月30日下午3:49:12
     *@param ctx
     *@param msg
     *@throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx,Object msg) throws Exception{
        if (msg instanceof FullHttpRequest) {
            handleHttpRequest (ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            ctx.fireChannelRead (((WebSocketFrame) msg).retain ());
        }
    }

    /**
     *@Description: TODO(处理Http请求，主要是完成HTTP协议到Websocket协议的升级) 
     *@Author: wangchengjie
     *@Since: 2019年3月30日下午3:49:32
     *@param ctx
     *@param req
     */
    private void handleHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req){
        if (!req.decoderResult ().isSuccess ()) {
            sendHttpResponse (ctx, req, new DefaultFullHttpResponse (HttpVersion.HTTP_1_1,HttpResponseStatus.BAD_REQUEST));
            return;
        }

        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory ("ws:/" + ctx.channel () + "/websocket",null,false);
        WebSocketServerHandshaker handshaker = wsFactory.newHandshaker (req);
        Constant.webSocketHandshakerMap.put (ctx.channel ().id ().asLongText (), handshaker);

        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse (ctx.channel ());
        } else {
            handshaker.handshake (ctx.channel (), req);
        }
    }

    /**
     *@Description: TODO(返回应答给客户端) 
     *@Author: wangchengjie
     *@Since: 2019年3月30日下午3:49:53
     *@param ctx
     *@param req
     *@param res
     */
    private void sendHttpResponse(ChannelHandlerContext ctx,FullHttpRequest req,DefaultFullHttpResponse res){
        // 返回应答给客户端
        if (res.status ().code () != 200) {
            ByteBuf buf = Unpooled.copiedBuffer (res.status ().toString (), CharsetUtil.UTF_8);
            res.content ().writeBytes (buf);
            buf.release ();
        }
        // 如果是非Keep-Alive，关闭连接
        boolean keepAlive = HttpUtil.isKeepAlive (req);
        ChannelFuture f = ctx.channel ().writeAndFlush (res);
        if (!keepAlive) {
            f.addListener (ChannelFutureListener.CLOSE);
        }
    }

    /**
     *@Description: TODO(异常处理，关闭channel) 
     *@Author: wangchengjie
     *@Since: 2019年3月30日下午3:50:04
     *@param ctx
     *@param cause
     *@throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause) throws Exception{
        cause.printStackTrace ();
        ctx.close ();
    }
}
